import json
import logging
import requests
from ding import tools, data
from smtplib import SMTP_SSL
from email.mime.text import MIMEText
import binascii
import re
import datetime


class Notice:
    RESOLVED_TITLE = '恢复正常'
    FIRING_TITLE = '异常告警'
    WARNING_TITLE = '风险警告'
    MSG_MERGE = []

    def __init__(self, alert_msg: dict, group: str):
        """
        处理告警信息，并推送至对应平台
        :param alert_msg: 需要推送的消息
        :param group: 告警分组
        """
        self.group = group
        self.alert_msg = alert_msg

    def text_temp(self, filter_msg: dict):
        """
        纯文本模板
        :param filter_msg:
        :return:
        """
        format_msg = ''
        if self.alert_msg.get('severity') in 'warning' and self.alert_msg.get('status') in 'firing':
            format_msg = f'{self.WARNING_TITLE}\n\n' + format_msg
        elif self.alert_msg.get('status') in 'firing':
            format_msg = f'{self.FIRING_TITLE}\n\n' + format_msg
        elif self.alert_msg.get('status') in 'resolved':
            format_msg = f'{self.RESOLVED_TITLE}\n\n' + format_msg

        for alert_key in filter_msg:
            format_msg = format_msg + alert_key + ": " + filter_msg[alert_key] + "\n"
        return format_msg

    def md_temp(self, platform: str, filter_msg: dict):
        """
        根据平台格式化markdown
        :param platform:
        :param filter_msg:
        :return:
        """
        format_msg = ''
        status = self.alert_msg.get('status')
        severity = self.alert_msg.get('severity')

        # 格式化标题
        if platform == 'feishu':
            if severity == 'warning' and status == 'firing':
                format_msg = f'<font color="grey"> **{self.WARNING_TITLE}** </font>\n' + format_msg
            elif status == 'firing':
                format_msg = f'<font color="red"> **{self.FIRING_TITLE}** </font>\n' + format_msg
            elif status == 'resolved':
                format_msg = f'<font color="green"> **{self.RESOLVED_TITLE}** </font>\n' + format_msg
        elif platform == 'dingding':
            if severity == 'warning' and status == 'firing':
                format_msg = f'### <font color=#FFD700 face="黑体">{self.WARNING_TITLE}</font>\n' + format_msg
            elif status == 'firing':
                format_msg = f'### <font color=#FF0000 face="黑体">{self.FIRING_TITLE}</font>\n' + format_msg
            elif status == 'resolved':
                format_msg = f'### <font color=#00FF00 face="黑体">{self.RESOLVED_TITLE}</font>\n' + format_msg

        # 格式化字段
        for alert_key in filter_msg:
            format_msg += "**" + str(alert_key) + ":** " + str(filter_msg[alert_key]) + "\n"
            # logging.debug("调试" + str(alert_key))
            if platform == 'dingding':
                format_msg += '\n'
        return format_msg

    def send_feishu(self, format_msg: str):
        """
        告警信息推送至飞书
        :param format_msg:
        """
        key = tools.cf_connect('feishu', 'key')
        group = tools.cf_connect('feishu', self.group)
        headers = {'Content-Type': 'application/json;charset=utf-8'}
        api = 'https://open.feishu.cn/open-apis/bot/v2/hook/' + group.split(',', 1)[0]

        # 格式化@
        pattern = r"ou_([a-z0-9]+)\(([^)]+)\)"
        result = re.findall(pattern, group)
        at_s = "".join(f"<at id=ou_{openid}>{name}</at>" for openid, name in result)
        body = {
            "msg_type": "interactive",
            "is_short": "false",
            "card": {
                "elements": [{
                    "tag": "markdown",
                    "content": key + format_msg + at_s,
                }]
            }
        }

        response = requests.post(api, json.dumps(body), headers=headers).content
        logging.info(f'告警信息推送 {response.decode("utf-8")}')

    def send_ding_ding(self, format_msg: str):
        """
        告警信息推送至钉钉
        :param format_msg:
        """
        group = tools.cf_connect('dingding', self.group)
        key = tools.cf_connect('dingding', 'key')
        headers = {'Content-Type': 'application/json;charset=utf-8'}
        api = 'https://oapi.dingtalk.com/robot/send?access_token=' + group.split(',', 1)[0]
        if not key:
            key = self.alert_msg.get('监控项')
        elif not key and self.alert_msg.get('监控项'):
            key = '告警'

        try:
            phones = group.split(',', 1)[1]
            phone_at = ', '.join([f"@{num}" for num in phones.split(',')])
            body = {
                "msgtype": "markdown",
                "markdown": {
                    "title": key,
                    "text": format_msg + phone_at.rstrip(",")
                },
                "at": {
                    "atMobiles": phones.split(','),
                    "isAtAll": "false"
                }
            }
        except IndexError:
            body = {
                "msgtype": "markdown",
                "markdown": {
                    "title": key,
                    "text": format_msg
                },
                "at": {
                    "isAtAll": "false"
                }
            }
        response = requests.post(api, json.dumps(body), headers=headers).content
        push_state = json.loads(response.decode("utf-8"))
        if push_state['errmsg'] in 'ok':
            logging.info("告警推送成功")
        else:
            logging.error(f'告警推送失败 {response.decode("utf-8")}')

    def task_temp(self):
        """
        每周任务推送
        :return:
        """
        push_dest = tools.cf_connect('settings', 'push_dest')

        if not push_dest and not data.db_status():
            logging.error("push配置填写错误或ES未启用!")
            return

        push_dest_mapping = {
            "email": self.send_mail,
            "dingding": self.send_ding_ding,
            "feishu": self.send_feishu
        }
        send_func = push_dest_mapping.get(push_dest)
        today = datetime.date.today()
        last_week_start = today - datetime.timedelta(days=today.weekday() + 7)
        last_week_end = today - datetime.timedelta(days=today.weekday() + 1)
        title = (f'## <font color=#0000FF face="黑体">告警周报</font>\n\n各位上午好，以下是上周告警情况('
                 f'{last_week_start}-{last_week_end}): \n\n')
        format_msg = title
        for alert in self.alert_msg:
            format_msg += f'{alert}: **{self.alert_msg[alert]}**次\n\n'
        send_func(format_msg)

    @staticmethod
    def send_mail(format_msg: str):
        """
        将告警信息推送至邮箱
        :param format_msg: 邮件内容
        """
        user = tools.cf_connect('email', 'user')
        password = tools.cf_connect('email', 'password')
        host = tools.cf_connect('email', 'host')
        port = tools.cf_connect('email', 'port')
        addressee = tools.cf_connect('email', 'addressee')
        subject = tools.cf_connect('email', 'subject')
        send_msg = MIMEText(format_msg, 'plain', _charset="utf-8")
        send_msg["Subject"] = subject
        send_msg["from"] = tools.cf_connect('email', 'sender')

        try:
            password = tools.salsa_decrypt(password).decode()
        except binascii.Error as ex:
            logging.error(f"email密码未加密! {ex}")
        else:
            with SMTP_SSL(host, port) as smtp:
                # 登录发邮件服务器
                smtp.login(user=user, password=password)
                # 发送、接收邮件配置
                smtp.sendmail(from_addr=user, to_addrs=addressee.split(','), msg=send_msg.as_string())
                logging.info('邮件已发送!')

    def field_filter(self):
        """
        推送消息字段过滤
        :return:
        """
        custom_push = tools.cf_connect().options('custom_push')
        if not custom_push:
            return self.alert_msg
        keys = set(self.alert_msg.keys())
        ff = list(keys.intersection(set(custom_push)))
        return {k: v for k, v in self.alert_msg.items() if k not in ff}

    def translate(self):
        """
        对字段进行转义再插入alert_msg
        :return:
        """
        try:
            trans_field = dict(tools.cf_connect().items('translate'))
            if not trans_field:
                return self.alert_msg
            self.alert_msg = {trans_field.get(k, k): v for k, v in self.alert_msg.items()}
        except Exception as e:
            logging.error(f"Failed to translate: {e}")

    def push_center(self):
        """
        消息推送中心，根据配置文件决定是否推送、进行字段过滤和选择推送平台
        """
        push_dest_mapping = {
            "email": (self.text_temp, self.send_mail),
            "dingding": (self.md_temp, self.send_ding_ding),
            "feishu": (self.md_temp, self.send_feishu),
        }

        push_dest = tools.cf_connect('settings', 'push_dest')

        if push_dest not in push_dest_mapping:
            logging.error("未选择告警推送方式或push配置填写错误!")
            return
        logging.info(f"告警项：{self.alert_msg.get('alertname')}")
        format_func, send_func = push_dest_mapping[push_dest]
        self.translate()
        format_msg = format_func(push_dest, self.field_filter())
        send_func(format_msg)
